package com.daiyang.campus_lucky_draw.service.impl;

import com.daiyang.campus_lucky_draw.common.enums.ActivityPrizeStatusEnum;
import com.daiyang.campus_lucky_draw.common.enums.ActivityStatusEnum;
import com.daiyang.campus_lucky_draw.common.enums.ActivityUserStatusEnum;
import com.daiyang.campus_lucky_draw.common.error.ServiceErrorCodeConstants;
import com.daiyang.campus_lucky_draw.common.exception.ServiceException;
import com.daiyang.campus_lucky_draw.common.enums.ActivityPrizeTiersEnum;
import com.daiyang.campus_lucky_draw.common.utils.JacksonUtil;
import com.daiyang.campus_lucky_draw.common.utils.RedisUtil;
import com.daiyang.campus_lucky_draw.contreller.param.CreateActivityParam;
import com.daiyang.campus_lucky_draw.contreller.param.CreatePrizeByActivityParam;
import com.daiyang.campus_lucky_draw.contreller.param.CreateUserByActivityParam;
import com.daiyang.campus_lucky_draw.contreller.param.PageListParam;
import com.daiyang.campus_lucky_draw.mapper.*;
import com.daiyang.campus_lucky_draw.mapper.DO.ActivityDO;
import com.daiyang.campus_lucky_draw.mapper.DO.ActivityPrizeDO;
import com.daiyang.campus_lucky_draw.mapper.DO.PrizeDO;
import com.daiyang.campus_lucky_draw.service.ActivityService;
import com.daiyang.campus_lucky_draw.service.dto.ActivityDTO;
import com.daiyang.campus_lucky_draw.service.dto.ActivityDetailDTO;
import com.daiyang.campus_lucky_draw.service.dto.CreateActivityDTO;
import com.daiyang.campus_lucky_draw.service.dto.PageListDTO;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;


@Slf4j
@Service
public class ActivityServiceImpl  implements ActivityService {
    private static final Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);

    public static final String ACTIVITY_PREFIX = "ACTIVITY_";
    private static final long ACTIVITY_EFFECTIVE_TIME = 60 * 60 * 24 * 3;
    @Autowired
    UserMapper userMapper;
    @Autowired
    PrizeMapper prizeMapper;
    @Autowired
    ActivityMapper activityMapper;
    @Autowired
    ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateActivityDTO createActivity(CreateActivityParam request) {
        // 校验活动信息: 课件中省略... 详细代码⻅源码
        checkActivityInfo(request);
        // 创建活动
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(request.getActivityName());
        activityDO.setDescription(request.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);
        // 关联活动商品
        List<CreatePrizeByActivityParam> prizeParamList =
                request.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = new ArrayList<>();
        prizeParamList.forEach(param->{
            ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
            activityPrizeDO.setActivityId(activityDO.getId());
            activityPrizeDO.setPrizeId(param.getPrizeId());
            activityPrizeDO.setPrizeAmount(param.getPrizeAmount());
            activityPrizeDO.setPrizeTiers(param.getPrizeTiers());
            activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
            activityPrizeDOList.add(activityPrizeDO);
        });
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        // 关联活动⼈员
        List<CreateUserByActivityParam> userParamList =
                request.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = new ArrayList<>();
        userParamList.forEach(param->{
            ActivityUserDO activityUserDO = new ActivityUserDO();
            activityUserDO.setActivityId(activityDO.getId());
            activityUserDO.setUserId(param.getUserId());
            activityUserDO.setUserName(param.getUserName());
            activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
            activityUserDOList.add(activityUserDO);
        });
        activityUserMapper.batchInsert(activityUserDOList);
        // 查询奖品信息列表
        List<Long> prizeIds = activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.selectByIdList(prizeIds);
        // 将活动以及关联的奖品缓存⾄redis
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDO, activityUserDOList,
                prizeDOList, activityPrizeDOList);
        cacheActivity(detailDTO);
        // 返回活动id
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;


    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageListParam request) {
        int count = activityMapper.count();
        List<ActivityDO> activities =
                activityMapper.queryActivitiesByPage(request.offset(),
                        request.getPageSize());
        List<ActivityDTO> records = new ArrayList<>();
        for (ActivityDO activityDO : activities) {
            ActivityDTO activityDTO = new ActivityDTO();
            activityDTO.setActivityId(activityDO.getId());
            activityDTO.setActivityName(activityDO.getActivityName());
            activityDTO.setDescription(activityDO.getDescription());

            activityDTO.setStatus(ActivityStatusEnum.fromName(activityDO.getStatus()));
            records.add(activityDTO);
        }
        return new PageListDTO(count, records);
    }

    @Override
    public ActivityDetailDTO  getActivityDetail(Long activityId) {
        if(null == activityId) {
            throw new
                    ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_IS_EMPTY);
        }
        // 从redis缓存中获取
        ActivityDetailDTO activityDetailDTO = getActivityFromCache(activityId);
        if (null != activityDetailDTO) {
            logger.info("从redis缓存中获取活动信息成功：{}",
                    JacksonUtil.writeValueAsString(activityDetailDTO));
            return activityDetailDTO;
        }
        //如果redis不存在，查表

        //活动表

        ActivityDO activityDO = activityMapper.selectById(activityId);
        logger.info("从数据库中获取活动信息成功：{}",
                JacksonUtil.writeValueAsString(activityDO));

        //活动奖品表

        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);
        logger.info("从数据库中获取活动奖品信息成功：{}",
                JacksonUtil.writeValueAsString(apDOList));

        //活动人员表

        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        logger.info("从数据库中获取活动人员信息成功：{}",
                JacksonUtil.writeValueAsString(auDOList));


        // 奖品表：先获取要查询的奖品id

        List<Long> prizeids=apDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList =prizeMapper.selectByIdList(prizeids);


        //整合活动详细信息，存放redis
        activityDetailDTO = convertToActivityDetailDTO(activityDO,auDOList,pDOList,apDOList);
        cacheActivity(activityDetailDTO);

        return activityDetailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if(activityId==null) {
            logger.warn("缓存活动信息失败，activityId为空");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }

        // 查询表数据：活动表、关联奖品、关联人员、奖品信息表
        ActivityDO aDO = activityMapper.selectById(activityId);
        if(null==aDO) {
            logger.warn("缓存活动信息失败，活动不存在，activityId={}",activityId);
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        // 活动奖品表
        List<ActivityPrizeDO> apDOList =  activityPrizeMapper.selectByActivityId(activityId);
        // 活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        // 奖品表: 先获取要查询的奖品id
        List<Long> prizeIds = apDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        // 整合活动详细信息，存放redis
        cacheActivity(
                convertToActivityDetailDTO(aDO, auDOList,
                        pDOList, apDOList));
    }


    /**
     * 根据活动id从缓存中获取活动详细信息
     *
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if (null == activityId) {
            logger.warn("获取缓存活动数据的activityId为空！");
            return null;
        }
        try {
            String str = (String) redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(str)) {
                logger.info("获取的缓存活动数据为空！key={}", ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtil.readValue(str, ActivityDetailDTO.class);
        } catch (Exception e) {
            logger.error("从缓存中获取活动信息异常，key={}", ACTIVITY_PREFIX + activityId, e);
            return null;
        }

    }
    /**
     * 将活动详细信息缓存到redis
     *
     * @param detailDTO
     */

    private void cacheActivity(ActivityDetailDTO detailDTO) {

            // key: ACTIVITY_12
            // value: ActivityDetailDTO(json)
            if (null == detailDTO || null == detailDTO.getActivityId()) {
                logger.warn("要缓存的活动信息不存在!");
                return;
            }

            try {
                redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                        JacksonUtil.writeValueAsString(detailDTO),
                        ACTIVITY_EFFECTIVE_TIME);
            } catch (Exception e) {
                logger.error("缓存活动异常，ActivityDetailDTO={}",
                        JacksonUtil.writeValueAsString(detailDTO),
                        e);
            }
    }

    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.fromName(activityDO.getStatus()));

        // apDO: {prizeId，amount, status}, {prizeId，amount, status}
        // pDO: {prizeid, name....},{prizeid, name....},{prizeid, name....}
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList
                .stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(apDO.getPrizeId());
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    // 如果PrizeDO为空，不执行当前方法，不为空才执行
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.fromName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.fromName(apDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());
        detailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream()
                .map(auDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(auDO.getUserId());
                    userDTO.setUserName(auDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.fromName(auDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);
        return detailDTO;
    }

    private void checkActivityInfo(CreateActivityParam param) {
        if (null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }
        // 人员id在人员表中是否存在
        // 1 2 3  ->  1 2
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existUserIds = userMapper.selectExistByIds(userIds);
        if (CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        // 奖品id在奖品表中是否存在
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);
        if (CollectionUtils.isEmpty(existPrizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id -> {
            if (!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        // 人员数量大于等于奖品数量
        // 2个奖品 2 1
        int userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount) // 2 1
                .sum();
        if (userAmount < prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }


        // 校验活动奖品等奖有效性
        param.getActivityPrizeList().forEach(prize -> {
            if (null == ActivityPrizeTiersEnum.fromName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });

    }
}
